A database is a collection of data that may be retrieved by users. In order to retrieve data from a database, users may formulate a query that specifies the data to be retrieved and submit the query to a database engine. The database engine interacts directly with the database to process the query and return the data that the query specifies, called a query result. Instead of directly formulating and submitting a query, many users interact with a database engine via a database front end. Database front ends provide user interfaces that generally help the user to formulate database queries, submit queries to the database engine for processing, and display query results. Query results are generally organized as a number of rows, each containing row data. All of the rows of a query result are known collectively as its "result set."
For a result set containing a large number of rows, it is unusual for a user to review all of the rows at one time. Indeed, sometimes a user reviews only a few rows from a large result set. This observation is the basis for a conventional approach in which the database engine generates only those rows of the result set that the user is presently requesting to display using the front end. The engine then produces additional rows of the result set in response to further user requests to display rows using the front end. This approach, however, requires the database engine to maintain extensive state information about the status of the query until the user finishes reviewing the query results. Further, the need to reinvoke the database engine when processing each subsequent user request for results causes response time for such requests to be relatively high.
In an alternative approach, the database engine performs the query as a single operation, and the query results produced by performing the query are maintained in memory in their entirety by the front end or an intermediary program while the user is using the front end to review them. For large result sets, this technique can require excessive amounts of RAM and/or excessive levels of virtual memory paging.
Given the drawbacks of these conventional approaches, a scheme for effectively representing query results in a limited amount of memory would be desirable.
Because embodiments of Applicants' invention described hereinafter are implemented using object-oriented programming, a brief discussion of object-oriented programming techniques follows.
Two common characteristics of object-oriented programming languages are support for data encapsulation and data type inheritance. Data encapsulation refers to the binding of functions and data. Inheritance refers to the ability to declare a data type in terms of other data types. In the C++ language, data encapsulation and inheritance are supported through the use of classes. A class is a user-defined type. A class declaration describes the data members and function members of the class. A function member is also referred to as a method of a class. The data members and function members of a class are bound together in that the function operates on an instance of the class. An instance of a class is also called an object of the class. Thus, a class provides a definition for a group of objects with similar properties and common behavior.
To allocate storage for an object of a particular type (class), an object is instantiated. Once instantiated, data can be assigned to the data members of the particular object. Also, once instantiated, the function members of the particular object can be invoked to access and manipulate the data members. Thus, in this manner, the function members implement the behavior of the object, and the object provides a structure for encapsulating data and behavior into a single entity.
To support the concept of inheritance, classes may be derived from (based upon the declaration of) other classes. A derived class is a class that inherits the characteristics--data members and function members--of its base classes. A class that inherits the characteristics of another class is a derived class. A class that does not inherit the characteristics of another class is a primary (root) class. A class whose characteristics are inherited by another class is a base class. A derived class may inherit the characteristics of several classes; that is, a derived class may have several base classes. This is referred to as multiple inheritance.
A class may also specify whether its function members are virtual. Declaring that a function member is virtual means that the function can be overridden by a function of the same name and type in a derived class. If a virtual function is declared without providing an implementation, then it is referred to as a pure virtual function. If a class specifies a pure virtual function, then any derived class needs to specify an implementation for that function member before that function member may be invoked. A class which contains at least one pure virtual function member is an abstract class.
An advantage of using object-oriented techniques is that these techniques can be used to facilitate the sharing of objects. To allow an object of an arbitrary class to be shared with a client program, interfaces are defined through which an object can be accessed without the need for the client program to have access to the class definitions at compile time. An interface is a named set of logically related function members. In C++, an interface is an abstract class with no data members and whose virtual functions are all pure. Thus, an interface provides a published protocol for two programs to communicate. Interfaces are typically used for derivation: a program defines (implements) classes that provide implementations for the interfaces the classes are derived from. Thereafter, objects are created as instances of these derived classes. Objects instantiated from a derived class implementing particular interfaces are said to "support" the interfaces. An object supports one or more interfaces depending upon the desired functionality.